package cn.wen233.client.net;

import cn.wen233.basic.utils.IoUtils;
import cn.wen233.basic.utils.StringUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.*;
import java.util.*;
import java.util.zip.GZIPInputStream;

/**
 * http客户端
 *
 * @author wenei
 * @date 2021-10-25 10:38
 */
public class HttpClient {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class);

    private static final boolean ENABLE_HTTPS = Boolean.getBoolean("sunglow.tls.enable");

    public static String getPrefix() {
        if (ENABLE_HTTPS) {
            return "https://";
        }
        return "http://";
    }

    public static HttpResult request(String url, Map<String, String> paramValues, String method) {
        return request(url, null, paramValues, null, "UTF-8", method);
    }

    public static HttpResult request(String url, List<String> headers, Map<String, String> paramValues, String body, String encoding, String method) {
        HttpURLConnection connection = null;
        encoding = encoding == null ? "UTF-8" : encoding;
        try {
            String encodedContent = encodingParams(paramValues, encoding);
            url += (StringUtils.isEmpty(encodedContent)) ? "" : ("?" + encodedContent);
            connection = (HttpURLConnection) new URL(url).openConnection();

            setHeaders(connection, headers, encoding);
            connection.setConnectTimeout(3000);
            connection.setReadTimeout(50000);
            connection.setRequestMethod(method);
            if (!StringUtils.isEmpty(body)) {
                byte[] bytes = body.getBytes();
                connection.setRequestProperty("Content-Length", String.valueOf(bytes.length));
                connection.getOutputStream().write(bytes, 0, bytes.length);
                connection.getOutputStream().flush();
                connection.getOutputStream().close();
            }
            connection.connect();
            LOGGER.debug("Request from server: " + url);
            return getResult(connection);
        } catch (ConnectException e) {
            return new HttpResult(-1, "服务端连接失败", Collections.emptyMap());
        } catch (Exception e) {
            try {
                if (connection != null) {
                    LOGGER.warn("failed to request " + connection.getURL() + " from " + InetAddress.getByName(connection.getURL().getHost()).getHostAddress());
                }
            } catch (UnknownHostException e1) {
                LOGGER.error("[NA] failed to request ", e1);
            }
            LOGGER.error("[NA] failed to request ", e);
            return new HttpResult(500, e.toString(), Collections.emptyMap());
        } finally {
            IoUtils.closeQuietly(connection);
        }
    }

    public static HttpResult getResult(HttpURLConnection connection) throws IOException {
        int responseCode = connection.getResponseCode();
        InputStream inputStream = null;
        if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
            inputStream = connection.getInputStream();
        } else {
            inputStream = connection.getErrorStream();
        }
        // 获取响应的Headers
        Map<String, String> responseHeaders = new HashMap<>(connection.getHeaderFields().size());
        for (Map.Entry<String, List<String>> entry : connection.getHeaderFields().entrySet()) {
            responseHeaders.put(entry.getKey(), entry.getValue().get(0));
        }
        // 如果Response的编码格式为gzip
        String encodingGzip = "gzip";
        if (encodingGzip.equals(responseHeaders.get("Content-Encoding"))) {
            inputStream = new GZIPInputStream(inputStream);
        }
        return new HttpResult(responseCode, IoUtils.toString(inputStream, getCharset(connection)), responseHeaders);
    }

    private static String getCharset(HttpURLConnection connection) {
        String contentType = connection.getContentType();
        if (StringUtils.isEmpty(contentType)) {
            return "UTF-8";
        }

        String[] values = contentType.split(",");
        if (values.length == 0) {
            return "UTF-8";
        }

        String charset = "UTF-8";
        for (String value : values) {
            value = value.trim();
            if (value.toLowerCase().startsWith("charset=")) {
                charset = value.substring("charset=".length());
            }
        }
        return charset;
    }

    private static String encodingParams(Map<String, String> paramValues, String encoding)
            throws UnsupportedEncodingException {
        if (paramValues == null || paramValues.isEmpty()) {
            return "";
        }
        paramValues.put("encoding", encoding);
        StringBuilder builder = new StringBuilder();
        // 遍历所有param
        for (Map.Entry<String, String> entry : paramValues.entrySet()) {
            if (StringUtils.isEmpty(entry.getValue())) {
                continue;
            }
            builder.append(entry.getKey())
                    .append("=")
                    .append(URLEncoder.encode(entry.getValue(), encoding))
                    .append("&");
        }
        // 去掉最后一个&
        if (builder.length() > 0) {
            builder = builder.deleteCharAt(builder.length() - 1);
        }
        return builder.toString();
    }

    /**
     * 给HttpURLConnection设置Headers
     */
    private static void setHeaders(HttpURLConnection connection, List<String> headers, String encoding) {
        if (headers != null) {
            for (Iterator<String> iterator = headers.iterator(); iterator.hasNext();) {
                connection.addRequestProperty(iterator.next(), iterator.next());
            }
        }
        connection.setDoOutput(true);
        connection.addRequestProperty("Content-Type", "application/json");
        connection.addRequestProperty("Accept-Charset", encoding);
    }

    /**
     * http请求结果
     */
    @Data
    @NoArgsConstructor
    public static class HttpResult {

        private int code;

        private String content;

        private Map<String, String> responseHeaders;

        public HttpResult(int code, String content, Map<String, String> responseHeaders) {
            this.code = code;
            this.content = content;
            this.responseHeaders = responseHeaders;
        }

        public int getCode() {
            return code;
        }

        public String getContent() {
            return content;
        }

        public Map<String, String> getResponseHeaders() {
            return responseHeaders;
        }
    }

    public static void main(String[] args) {
        System.out.println(request("http://localhost:11112/api/namespaces", null, HttpMethod.GET).getContent());
    }
}
